﻿
namespace Anycmd.Host.Impl
{
    using AC;
    using AC.Identity.Messages;
    using AC.Messages;
    using AC.ValueObjects;
    using Anycmd.AC;
    using Dapper;
    using Exceptions;
    using Model;
    using Repositories;
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Text;
    using Util;

    public class DefaultRBACService : IRBACService
    {
        private readonly IAppHost host;

        public DefaultRBACService(IAppHost host)
        {
            this.host = host;
        }

        public void AddUser(IAccountCreateInput input)
        {
            host.Handle(new AddAccountCommand(input));
        }

        public void DeleteUser(Guid accountID)
        {
            host.Handle(new RemoveAccountCommand(accountID));
        }

        public void AddRole(IRoleCreateInput input)
        {
            host.Handle(new AddRoleCommand(input));
        }

        public void DeleteRole(Guid roleID)
        {
            host.Handle(new RemoveRoleCommand(roleID));
        }

        public void AssignUser(Guid accountID, Guid roleID)
        {
            host.Handle(new AddPrivilegeBigramCommand(new PrivilegeBigramCreateInput
            {
                Id = Guid.NewGuid(),
                SubjectType = ACSubjectType.Account.ToName(),
                SubjectInstanceID = accountID,
                ObjectType = ACObjectType.Role.ToName(),
                ObjectInstanceID = roleID,
                PrivilegeConstraint = null,
                PrivilegeOrientation = 1
            }));
        }

        public void DeassignUser(Guid accountID, Guid roleID)
        {
            var repository = host.GetRequiredService<IRepository<PrivilegeBigram>>();
            var subjectType = ACSubjectType.Account.ToName();
            var objectType = ACObjectType.Role.ToName();
            var entity = repository.FindAll().FirstOrDefault(a => a.SubjectType == subjectType && a.SubjectInstanceID == accountID && a.ObjectType == objectType && a.ObjectInstanceID == roleID);
            if (entity == null)
            {
                return;
            }
            host.Handle(new RemovePrivilegeBigramCommand(entity.Id));
        }

        public void GrantPermission(Guid functionID, Guid roleID)
        {
            host.Handle(new AddPrivilegeBigramCommand(new PrivilegeBigramCreateInput
            {
                Id = Guid.NewGuid(),
                SubjectType = ACSubjectType.Role.ToName(),
                SubjectInstanceID = roleID,
                ObjectType = ACObjectType.Function.ToName(),
                ObjectInstanceID = functionID,
                PrivilegeConstraint = null,
                PrivilegeOrientation = 1
            }));
        }

        public void RevokePermission(Guid functionID, Guid roleID)
        {
            var repository = host.GetRequiredService<IRepository<PrivilegeBigram>>();
            var subjectType = ACSubjectType.Role.ToName();
            var objectType = ACObjectType.Function.ToName();
            var entity = repository.FindAll().FirstOrDefault(a => a.SubjectType == subjectType && a.SubjectInstanceID == roleID && a.ObjectType == objectType && a.ObjectInstanceID == functionID);
            if (entity == null)
            {
                return;
            }
            host.Handle(new RemovePrivilegeBigramCommand(entity.Id));
        }

        public IUserSession CreateSession(Guid sessionID, AccountState account)
        {
            var sessionService = host.GetRequiredService<IUserSessionService>();
            return sessionService.CreateSession(host, sessionID, account);
        }

        public void DeleteSession(Guid sessionID)
        {
            var sessionService = host.GetRequiredService<IUserSessionService>();
            sessionService.DeleteSession(host, sessionID);
        }

        public IReadOnlyCollection<RoleState> SessionRoles(Guid sessionID)
        {
            var result = new List<RoleState>();
            if (sessionID == host.UserSession.Id)
            {
                foreach (var item in host.UserSession.AccountPrivilege.AuthorizedRoles)
                {
                    result.Add(item);
                }
            }
            else
            {
                var sessionRepository = host.GetRequiredService<IRepository<UserSession>>();
                var entity = sessionRepository.GetByKey(sessionID);
                var session = new UserSessionState(host, entity);

                return session.AccountPrivilege.AuthorizedRoles;
            }

            return result;
        }

        public IReadOnlyCollection<FunctionState> SessionPermissions(Guid sessionID)
        {
            var result = new List<FunctionState>();
            if (sessionID == host.UserSession.Id)
            {
                foreach (var item in host.UserSession.AccountPrivilege.AuthorizedFunctions)
                {
                    result.Add(item);
                }
            }
            else
            {
                var sessionRepository = host.GetRequiredService<IRepository<UserSession>>();
                var entity = sessionRepository.GetByKey(sessionID);
                var session = new UserSessionState(host, entity);

                return session.AccountPrivilege.AuthorizedFunctions;
            }

            return result;
        }

        public void AddActiveRole(Guid roleID, Guid sessionID)
        {
            RoleState role;
            if (!host.RoleSet.TryGetRole(roleID, out role))
            {
                throw new ValidationException("给定标识的角色不存在" + roleID);
            }
            IUserSession session;
            if (sessionID == host.UserSession.Id)
            {
                session = host.UserSession;
            }
            else
            {
                var sessionRepository = host.GetRequiredService<IRepository<UserSession>>();
                var entity = sessionRepository.GetByKey(sessionID);
                session = new UserSessionState(host, entity);
            }
            if (session == null)
            {
                throw new ValidationException("给定标识的会话不存在" + sessionID);
            }
            session.AccountPrivilege.AddActiveRole(role);
        }

        public void DropActiveRole(Guid sessionID, Guid roleID)
        {
            RoleState role;
            if (!host.RoleSet.TryGetRole(roleID, out role))
            {
                throw new ValidationException("给定标识的角色不存在" + roleID);
            }
            IUserSession session;
            if (sessionID == host.UserSession.Id)
            {
                session = host.UserSession;
            }
            else
            {
                var sessionRepository = host.GetRequiredService<IRepository<UserSession>>();
                var entity = sessionRepository.GetByKey(sessionID);
                session = new UserSessionState(host, entity);
            }
            if (session == null)
            {
                throw new ValidationException("给定标识的会话不存在" + sessionID);
            }
            session.AccountPrivilege.DropActiveRole(role);
        }

        public bool CheckAccess(Guid sessionID, Guid functionID, IManagedObject obj)
        {
            var securityService = host.GetRequiredService<ISecurityService>();
            FunctionState function;
            if (!host.FunctionSet.TryGetFunction(functionID, out function))
            {
                throw new ValidationException("给定标识的功能不存在" + functionID);
            }
            IUserSession session;
            if (sessionID == host.UserSession.Id)
            {
                session = host.UserSession;
            }
            else
            {
                var sessionRepository = host.GetRequiredService<IRepository<UserSession>>();
                var entity = sessionRepository.GetByKey(sessionID);
                session = new UserSessionState(host, entity);
            }
            if (session == null)
            {
                throw new ValidationException("给定标识的会话不存在" + sessionID);
            }
            return securityService.Permit(session, function, obj);
        }

        public virtual IReadOnlyCollection<AccountState> AssignedUsers(Guid roleID)
        {
            // TODO:移除数据访问代码
            var sql =
@"SELECT  a.Id ,
            a.NumberID,
            a.LoginName ,
            a.CreateOn ,
            a.Name ,
            a.Code ,
            a.Email,
            a.QQ,
            a.Mobile
    FROM    dbo.Account AS a
            JOIN dbo.PrivilegeBigram AS ar ON a.Id = ar.SubjectInstanceID
                                              AND ar.SubjectType = 'Account'
                                              AND ar.ObjectType = 'Role' AND ar.ObjectInstanceID='" + roleID + @"'
    WHERE   a.DeletionStateCode = 0";
            EntityTypeState entityType;
            if (!host.EntityTypeSet.TryGetEntityType("AC", "Account", out entityType))
            {
                throw new CoreException("意外的实体类型码AC.Account");
            }
            Anycmd.Rdb.RdbDescriptor db;
            if (!host.Rdbs.TryDb(entityType.DatabaseID, out db))
            {
                throw new CoreException("意外的账户数据库标识" + entityType.DatabaseID);
            }
            using (var conn = db.GetConnection())
            {
                if (conn.State != ConnectionState.Open)
                {
                    conn.Open();
                }
                return conn.Query<AccountState>(sql).ToList();
            }
        }

        public IReadOnlyCollection<RoleState> AssignedRoles(Guid accountID)
        {
            var repository = host.GetRequiredService<IRepository<PrivilegeBigram>>();
            var subjectType = ACSubjectType.Account.ToName();
            var objectType = ACObjectType.Role.ToName();
            var privileges = repository.FindAll().Where(a => a.SubjectType == subjectType && a.SubjectInstanceID == accountID && a.ObjectType == objectType);
            var list = new List<RoleState>();
            foreach (var item in privileges)
            {
                RoleState role;
                if (host.RoleSet.TryGetRole(item.ObjectInstanceID, out role))
                {
                    list.Add(role);
                }
            }
            return list;
        }

        public virtual IReadOnlyCollection<AccountState> AuthorizedUsers(Guid roleID)
        {
            RoleState role;
            if (!host.RoleSet.TryGetRole(roleID, out role))
            {
                throw new ValidationException("给定标识的角色不存在" + roleID);
            }
            var roles = new HashSet<RoleState> { role };
            foreach (var item in host.RoleSet.GetAscendantRoles(role))
            {
                roles.Add(item);
            }
            StringBuilder sb = new StringBuilder();
            foreach (var item in roles)
            {
                if (sb.Length != 0)
                {
                    sb.Append(",");
                }
                sb.Append("'").Append(item.Id.ToString()).Append("'");
            }
            // TODO:移除数据访问代码
            var sql =
@"SELECT  a.Id ,
            a.NumberID,
            a.LoginName ,
            a.CreateOn ,
            a.Name ,
            a.Code ,
            a.Email,
            a.QQ,
            a.Mobile
    FROM    dbo.Account AS a
            JOIN dbo.PrivilegeBigram AS ar ON a.Id = ar.SubjectInstanceID
                                              AND ar.SubjectType = 'Account'
                                              AND ar.ObjectType = 'Role' AND ar.ObjectInstanceID IN (" + sb.ToString() + @")
    WHERE   a.DeletionStateCode = 0";
            EntityTypeState entityType;
            if (!host.EntityTypeSet.TryGetEntityType("AC", "Account", out entityType))
            {
                throw new CoreException("意外的实体类型码AC.Account");
            }
            Anycmd.Rdb.RdbDescriptor db;
            if (!host.Rdbs.TryDb(entityType.DatabaseID, out db))
            {
                throw new CoreException("意外的账户数据库标识" + entityType.DatabaseID);
            }
            using (var conn = db.GetConnection())
            {
                if (conn.State != ConnectionState.Open)
                {
                    conn.Open();
                }
                return conn.Query<AccountState>(sql).ToList();
            }
        }

        public IReadOnlyCollection<RoleState> AuthorizedRoles(Guid accountID)
        {
            AccountPrivilege accountPrivilege;
            if (accountID == host.UserSession.Account.Id)
            {
                accountPrivilege = host.UserSession.AccountPrivilege;
            }
            else
            {
                accountPrivilege = new AccountPrivilege(host, accountID);
            }
            return accountPrivilege.AuthorizedRoles;
        }

        public IReadOnlyCollection<FunctionState> RolePermissions(Guid roleID)
        {
            RoleState role;
            if (!host.RoleSet.TryGetRole(roleID, out role))
            {
                throw new ValidationException("给定标识的角色不存在" + roleID);
            }
            HashSet<FunctionState> functions = new HashSet<FunctionState>();
            foreach (var privilege in host.PrivilegeSet.Where(a => a.SubjectType == ACSubjectType.Role && a.SubjectInstanceID == roleID && a.ObjectType == ACObjectType.Function))
            {
                FunctionState f;
                if (host.FunctionSet.TryGetFunction(privilege.ObjectInstanceID, out f))
                {
                    functions.Add(f);
                }
            }
            foreach (var item in host.RoleSet.GetDescendantRoles(role))
            {
                foreach (var privilege in host.PrivilegeSet.Where(a => a.SubjectType == ACSubjectType.Role && a.SubjectInstanceID == item.Id && a.ObjectType == ACObjectType.Function))
                {
                    FunctionState f;
                    if (host.FunctionSet.TryGetFunction(privilege.ObjectInstanceID, out f))
                    {
                        functions.Add(f);
                    }
                }
            }
            return functions.ToList();
        }

        public IReadOnlyCollection<FunctionState> UserPermissions(Guid accountID)
        {
            AccountPrivilege accountPrivilege;
            if (accountID == host.UserSession.Account.Id)
            {
                accountPrivilege = host.UserSession.AccountPrivilege;
            }
            else
            {
                accountPrivilege = new AccountPrivilege(host, accountID);
            }
            return accountPrivilege.AuthorizedFunctions;
        }

        public IReadOnlyCollection<FunctionState> RoleOperationsOnObject(Guid roleID, IManagedObject obj)
        {
            RoleState role;
            if (!host.RoleSet.TryGetRole(roleID, out role))
            {
                throw new ValidationException("给定标识的角色不存在" + roleID);
            }
            HashSet<FunctionState> functions = new HashSet<FunctionState>();
            foreach (var item in host.RoleSet.GetDescendantRoles(role))
            {
                foreach (var privilege in host.PrivilegeSet.Where(a => a.SubjectType == ACSubjectType.Role && a.SubjectInstanceID == roleID && a.ObjectType == ACObjectType.Function))
                {
                    FunctionState f;
                    if (host.FunctionSet.TryGetFunction(privilege.ObjectInstanceID, out f))
                    {
                        functions.Add(f);
                    }
                }
            }
            // TODO:执行实体级策略筛选返回的功能列表
            return functions.ToList();
        }

        public IReadOnlyCollection<FunctionState> UserOperationsOnObject(Guid accountID, IManagedObject obj)
        {
            AccountPrivilege accountPrivilege;
            if (accountID == host.UserSession.Account.Id)
            {
                accountPrivilege = host.UserSession.AccountPrivilege;
            }
            else
            {
                accountPrivilege = new AccountPrivilege(host, accountID);
            }
            HashSet<FunctionState> functions = new HashSet<FunctionState>();
            foreach (var f in accountPrivilege.AuthorizedFunctions)
            {
                functions.Add(f);
            }
            // TODO:执行实体级策略筛选返回的功能列表
            return functions.ToList();
        }

        public void AddInheritance(Guid subjectRoleID, Guid objectRoleID)
        {
            host.Handle(new AddPrivilegeBigramCommand(new PrivilegeBigramCreateInput
            {
                Id = Guid.NewGuid(),
                SubjectType = ACSubjectType.Role.ToName(),
                SubjectInstanceID = subjectRoleID,
                ObjectType = ACObjectType.Role.ToName(),
                ObjectInstanceID = objectRoleID,
                PrivilegeConstraint = null,
                PrivilegeOrientation = 1
            }));
        }

        public void DeleteInheritance(Guid subjectRoleID, Guid objectRoleID)
        {
            var repository = host.GetRequiredService<IRepository<PrivilegeBigram>>();
            var subjectType = ACSubjectType.Role.ToName();
            var objectType = ACObjectType.Role.ToName();
            var entity = repository.FindAll().FirstOrDefault(a => a.SubjectType == subjectType && a.SubjectInstanceID == subjectRoleID && a.ObjectType == objectType && a.ObjectInstanceID == objectRoleID);
            if (entity == null)
            {
                return;
            }
            host.Handle(new RemovePrivilegeBigramCommand(entity.Id));
        }

        public void AddAscendant(Guid childRoleID, IRoleCreateInput parentRoleCreateInput)
        {
            host.Handle(new AddRoleCommand(parentRoleCreateInput));
            host.Handle(new AddPrivilegeBigramCommand(new PrivilegeBigramCreateInput
            {
                Id = Guid.NewGuid(),
                SubjectType = ACSubjectType.Role.ToName(),
                SubjectInstanceID = childRoleID,
                ObjectType = ACObjectType.Role.ToName(),
                ObjectInstanceID = parentRoleCreateInput.Id.Value,
                PrivilegeConstraint = null,
                PrivilegeOrientation = 1
            }));
        }

        public void AddDescendant(Guid parentRoleID, IRoleCreateInput childRoleCreateInput)
        {
            host.Handle(new AddRoleCommand(childRoleCreateInput));
            host.Handle(new AddPrivilegeBigramCommand(new PrivilegeBigramCreateInput
            {
                Id = Guid.NewGuid(),
                SubjectType = ACSubjectType.Role.ToName(),
                SubjectInstanceID = childRoleCreateInput.Id.Value,
                ObjectType = ACObjectType.Role.ToName(),
                ObjectInstanceID = parentRoleID,
                PrivilegeConstraint = null,
                PrivilegeOrientation = 1
            }));
        }

        public void CreateSSDSet(ISSDSetCreateInput input)
        {
            host.Handle(new AddSSDSetCommand(input));
        }
         
        public void DeleteSSDSet(Guid ssdSetID)
        {
            host.Handle(new RemoveSSDSetCommand(ssdSetID));
        }

        public void AddSSDRoleMember(Guid ssdSetID, Guid roleID)
        {
            host.Handle(new AddSSDRoleCommand(new SSDRoleCreateInput
            {
                Id = Guid.NewGuid(),
                RoleID = roleID,
                SSDSetID = ssdSetID
            }));
        }

        public void DeleteSSDRoleMember(Guid ssdRoleID)
        {
            host.Handle(new RemoveSSDRoleCommand(ssdRoleID));
        }

        public void DeleteSSDRoleMember(Guid ssdSetID, Guid roleID)
        {
            var ssdRoleRepository = host.GetRequiredService<IRepository<SSDRole>>();
            var entity = ssdRoleRepository.FindAll().FirstOrDefault(a => a.SSDSetID == ssdSetID && a.RoleID == roleID);
            if (entity == null)
            {
                return;
            }
            host.Handle(new RemoveSSDRoleCommand(entity.Id));
        }

        public void SetSSDCardinality(Guid ssdSetID, int cardinality)
        {
            SSDSetState state;
            if (!host.SSDSetSet.TryGetSSDSet(ssdSetID, out state))
            {
                throw new ValidationException("意外的静态责任分离角色集标识" + ssdSetID);
            }
            host.Handle(new UpdateSSDSetCommand(new SSDSetUpdateInput
            {
                Id = state.Id,
                Description = state.Description,
                IsEnabled = state.IsEnabled,
                Name = state.Name,
                SSDCard = cardinality
            }));
        }

        public IReadOnlyCollection<SSDRoleState> SSDRoleSets()
        {
            return host.SSDSetSet.GetSSDRoles();
        }

        public IReadOnlyCollection<RoleState> SSDRoleSetRoles(Guid ssdSetID)
        {
            SSDSetState ssdSet;
            if (!host.SSDSetSet.TryGetSSDSet(ssdSetID, out ssdSet))
            {
                throw new ValidationException("给定标识的静态责任分离角色集不存在" + ssdSetID);
            }
            var result = new List<RoleState>();
            foreach (var item in host.SSDSetSet.GetSSDRoles(ssdSet))
            {
                RoleState role;
                if (host.RoleSet.TryGetRole(item.RoleID, out role))
                {
                    result.Add(role);
                }
            }

            return result;
        }

        public int SSDRoleSetCardinality(Guid ssdSetID)
        {
            SSDSetState ssdSet;
            if (!host.SSDSetSet.TryGetSSDSet(ssdSetID, out ssdSet))
            {
                throw new ValidationException("意外的静态责任分离角色集标识" + ssdSetID);
            }
            return ssdSet.SSDCard;
        }

        public void CreateDSDSet(IDSDSetCreateInput input)
        {
            host.Handle(new AddDSDSetCommand(input));
        }

        public void DeleteDSDSet(Guid dsdSetID)
        {
            host.Handle(new RemoveDSDSetCommand(dsdSetID));
        }

        public void AddDSDRoleMember(Guid dsdSetID, Guid roleID)
        {
            host.Handle(new AddDSDRoleCommand(new DSDRoleCreateInput
            {
                Id = Guid.NewGuid(),
                RoleID = roleID,
                DSDSetID = dsdSetID
            }));
        }

        public void DeleteDSDRoleMember(Guid dsdRoleID)
        {
            host.Handle(new RemoveDSDRoleCommand(dsdRoleID));
        }

        public void DeleteDSDRoleMember(Guid dsdSetID, Guid roleID)
        {
            var dsdRoleRepository = host.GetRequiredService<IRepository<DSDRole>>();
            var entity = dsdRoleRepository.FindAll().FirstOrDefault(a => a.DSDSetID == dsdSetID && a.RoleID == roleID);
            if (entity == null)
            {
                return;
            }
            host.Handle(new RemoveDSDRoleCommand(entity.Id));
        }

        public void SetDSDCardinality(Guid dsdSetID, int cardinality)
        {
            DSDSetState state;
            if (!host.DSDSetSet.TryGetDSDSet(dsdSetID, out state))
            {
                throw new ValidationException("意外的动态责任分离角色集标识" + dsdSetID);
            }
            host.Handle(new UpdateDSDSetCommand(new DSDSetUpdateInput
            {
                Id = state.Id,
                Description = state.Description,
                IsEnabled = state.IsEnabled,
                Name = state.Name,
                DSDCard = cardinality
            }));
        }

        public IReadOnlyCollection<DSDRoleState> DSDRoleSets()
        {
            return host.DSDSetSet.GetDSDRoles();
        }

        public IReadOnlyCollection<RoleState> DSDRoleSetRoles(Guid dsdSetID)
        {
            DSDSetState dsdSet;
            if (!host.DSDSetSet.TryGetDSDSet(dsdSetID, out dsdSet))
            {
                throw new ValidationException("给定标识的动态责任分离角色集不存在" + dsdSetID);
            }
            var result = new List<RoleState>();
            foreach (var item in host.DSDSetSet.GetDSDRoles(dsdSet))
            {
                RoleState role;
                if (host.RoleSet.TryGetRole(item.RoleID, out role))
                {
                    result.Add(role);
                }
            }

            return result;
        }

        public int DSDRoleSetCardinality(Guid dsdSetID)
        {
            DSDSetState dsdSet;
            if (!host.DSDSetSet.TryGetDSDSet(dsdSetID, out dsdSet))
            {
                throw new ValidationException("意外的动态责任分离角色集标识" + dsdSetID);
            }
            return dsdSet.DSDCard;
        }
    }
}
